package com.yizisu.basemvvm.utils

import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.os.Build
import android.os.Environment
import android.text.TextUtils
import com.yizisu.basemvvm.app
import java.io.*
import java.lang.reflect.Field
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.*
import kotlin.collections.HashMap

/**
 * 全局异常捕获，并保存在本地文件中
 * path:内部存储空间/Android/data/包名/files/Documents/Crash/cash-xxxx.txt
 */
object CrashHelper : Thread.UncaughtExceptionHandler {

    override fun uncaughtException(t: Thread, e: Throwable) {
        //收集错误信息，保存到 sd 卡上
        errorInfo2SD(e)
        //杀掉进程，退出应用
        app.finishAllActivity(true)
    }


    private fun errorInfo2SD(e: Throwable) {
        //用于存储设备信息
        val mInfo: MutableMap<String, String> = HashMap()
        val pm: PackageManager = app.packageManager
        try {
            val info: PackageInfo =
                pm.getPackageInfo(app.packageName, PackageManager.GET_ACTIVITIES)
            // 获取版本信息
            val versionName =
                if (TextUtils.isEmpty(info.versionName)) "未设置版本名称" else info.versionName
            val versionCode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
                info.longVersionCode.toString() + ""
            } else {
                info.versionCode.toString() + ""
            }
            mInfo["versionName"] = versionName
            mInfo["versionCode"] = versionCode
            // 获取设备信息
            val fields: Array<Field> = Build::class.java.fields
            if (fields.isNotEmpty()) {
                for (field in fields) {
                    field.isAccessible = true
                    mInfo[field.name] = field.get(null).toString()
                }
            }
            // 存储信息到 sd 卡指定目录
            saveErrorInfo(mInfo, e)
        } catch (e: PackageManager.NameNotFoundException) {
            e.printStackTrace()
        } catch (e: IllegalAccessException) {
            e.printStackTrace()
        }
    }

    private fun saveErrorInfo(
        mInfo: MutableMap<String, String>,
        e: Throwable
    ) {
        //格式化时间，作为Log文件名
        val dateFormat: DateFormat =
            SimpleDateFormat("yyyy年MM月dd日HH时mm分ss秒", Locale.getDefault())
        val stringBuffer = StringBuffer()
        for ((keyName, value) in mInfo) {
            stringBuffer.append("$keyName=$value\n")
        }
        stringBuffer.append("\n-----Crash Log Begin-----\n")
        val stringWriter = StringWriter()
        val writer = PrintWriter(stringWriter)
        e.printStackTrace(writer)
        var cause = e.cause
        while (cause != null) {
            cause.printStackTrace(writer)
            cause = e.cause
        }
        writer.close()
        val string: String = stringWriter.toString()
        stringBuffer.append(string)
        stringBuffer.append("\n-----Crash Log End-----")
        val format = dateFormat.format(Date())
        val dirPath =
            app.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS)!!.path.trimEnd('/') + "/" + "Crash"
        val dir = File(dirPath)
        if (!dir.exists()) {
            dir.mkdirs()
        }
        val path = dirPath.trimEnd('/') + "/crash-$format.txt"
        var fou: FileOutputStream? = null
        try {
            fou = FileOutputStream(File(path))
            fou.write(stringBuffer.toString().toByteArray())
            fou.flush()
        } catch (e1: FileNotFoundException) {
            e1.printStackTrace()
        } catch (e1: IOException) {
            e1.printStackTrace()
        } finally {
            try {
                fou?.close()
            } catch (e1: IOException) {
                e1.printStackTrace()
            }
        }
    }
}